<template>
  <div
    class="markPage"
    v-loading="isLoading && { text: loadingText }"
    :element-loading-text="loadingText"
  >
    <Tools @add-geometry="handleAddGeometry" @save="handleSave"></Tools>
    <div class="bottom">
      <div class="left">
        <div ref="viewpointRef" class="viewpoint"></div>
        <Labels
          :labels="geometries"
          :current-id="currentLabelId"
          @delete-label="handleDeleteLabel"
          @edit-label="handleEditLabel"
          @label-click="handleLabelClick"
          @view-label="handleViewLabel"
        ></Labels>
      </div>
      <div class="right">
        <div ref="containerRef" class="container">
          <div class="mainViewport" id="mainViewport"></div>
          <div class="viewportOne" id="viewportOne" ref="viewportOneRef">
            <span>正</span>
          </div>
          <div class="viewportTwo" id="viewportTwo"><span>左</span></div>
          <div class="viewportThree" id="viewportThree"><span>俯</span></div>
          <Transform
            v-model:transform="currentTransform"
            v-show="currentLabelId"
            v-model:position-step="positionStep"
            v-model:scale-step="scaleStep"
          ></Transform>
        </div>
      </div>
    </div>
    <ViewDialog
      ref="viewDialogRef"
      v-model:visible="isViewVisible"
      @opened="handleOpened"
    ></ViewDialog>
  </div>
</template>
<script lang="tsx" setup>
import { ElMessage, ElMessageBox } from 'element-plus'
import * as THREE from 'three'
import { computed, onMounted, ref, watch } from 'vue'
import { useRoute } from 'vue-router'

import { useGltfModel } from '@/hooks/useGltfModel'
import { useViewpoint } from '@/hooks/useViewpoint'
import { downloadModel } from '@/http/models'
import { addLabelSegment, getLabels } from '@/http/segmentLabel'

import Labels from './components/Labels.vue'
import Tools from './components/Tools.vue'
import Transform from './components/Transform.vue'
import ViewDialog from './components/ViewDialog.vue'
import {
  GeometryItem,
  GeometryType,
  Operation,
  useLabel,
} from './hooks/useLabel'
import { useThree } from './hooks/useThree'

const route = useRoute()
const modelId = route.params.id as string
// const glbUrl = '/model/小源文件导出glb/未命名.glb'

const containerRef = ref<HTMLDivElement>()
const viewpointRef = ref<HTMLDivElement>()
const viewportOneRef = ref<HTMLDivElement>()

// useStats(containerRef)
const { modelSize, modelCenter, threeModel, addModel, addLabel, controls } =
  useThree(containerRef)
useViewpoint(viewpointRef, threeModel, controls)

const loadingText = computed(() => {
  return `加载中... ${percentComplete.value}%`
})
const { loadModel, isLoading, percentComplete } = useGltfModel()
const {
  geometries,
  addBox,
  addSphere,
  addCylinder,
  checkGeometry,
  removeGeometry,
} = useLabel()

const handleDeleteLabel = async (item: GeometryItem) => {
  try {
    await ElMessageBox.confirm('删除该标签，是否继续?', '提示', {
      type: 'warning',
    })
    removeGeometry(item.id)
  } catch (error) {
    console.error(error)
  }
}

const handleLabelClick = (item: GeometryItem) => {
  handleCheckItem(item.id)
}

const isViewVisible = ref(false)
const viewDialogRef = ref<typeof ViewDialog>()
const handleViewLabel = async (item: GeometryItem) => {
  isViewVisible.value = true
  viewDialogRef.value?.setMesh(threeModel.value?.children[0], item.mesh)
}
const handleOpened = () => {
  viewDialogRef.value?.init()
}

const handleEditLabel = async (geoItem: GeometryItem) => {
  const { value } = await ElMessageBox.prompt('请输入标注类别', '修改类别', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    inputPattern: /^\S+$/,
    inputErrorMessage: '请输入正确类别',
  })
  const item = geometries.value.find((item) => item.id === geoItem.id)
  if (!item) return
  item.name = value
}

const handleAddGeometry = async (type: GeometryType) => {
  const { value } = await ElMessageBox.prompt('请输入标注类别', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    inputPattern: /^\S+$/,
    inputErrorMessage: '请输入正确类别',
  })
  let geometry: THREE.Mesh | null = null
  let id = ''
  const position = {
    x: modelCenter.value.x || 0,
    y: modelCenter.value.y || 0,
    z: modelCenter.value.z || 0,
  }
  if (type === 'box') {
    const item = addBox(
      value,
      undefined,
      position,
      modelSize.value.x,
      modelSize.value.y,
      modelSize.value.z,
    )
    geometry = item.mesh
    id = item.id
  } else if (type === 'sphere') {
    const radius =
      Math.max(modelSize.value.x, modelSize.value.y, modelSize.value.z) / 2
    const item = addSphere(value, undefined, position, radius, radius, radius)
    geometry = item.mesh
    id = item.id
  } else if (type === 'cylinder') {
    const radius = modelSize.value.z / 2
    const item = addCylinder(
      value,
      undefined,
      position,
      radius,
      modelSize.value.y,
      radius,
    )
    geometry = item.mesh
    id = item.id
  }
  if (geometry) {
    addLabel(geometry)
    handleCheckItem(id)
  }
}
// import {
//   INTERSECTION,
//   SUBTRACTION,
//   ADDITION,
//   Brush,
//   Evaluator,
// } from 'three-bvh-csg'
const handleCheckItem = (id: string) => {
  currentLabelId.value = id
  checkGeometry(id)
  setCurrentLabel(id)

  // const item = geometries.value.find((item) => item.id === id)
  // if (!item) return
  // if (!threeModel.value) return
  // const intRes = CSG.intersect(
  //   threeModel.value.children[0] as THREE.Mesh,
  //   item.mesh,
  // )
  // intRes.position.set(modelSize.value.x, 0, 0)
  // addLabel(intRes)
}

const currentLabelId = ref('')
const positionStep = ref(0.01)
const scaleStep = ref(0.01)
interface ITransformData {
  x: number
  y: number
  z: number
  scaleX: number
  scaleY: number
  scaleZ: number
}
const currentTransform = ref<ITransformData>({
  x: 0,
  y: 0,
  z: 0,
  scaleX: 1,
  scaleY: 1,
  scaleZ: 1,
})
watch(
  currentTransform,
  (value) => {
    handleSetMeshTransform(value)
  },
  { deep: true },
)

const handleSetMeshTransform = (value: ITransformData) => {
  const item = geometries.value.find((item) => item.id === currentLabelId.value)
  const currentMesh = item?.mesh
  console.log(currentMesh, value)
  currentMesh?.position.set(value.x, value.y, value.z)
  currentMesh?.scale.set(value.scaleX, value.scaleY, value.scaleZ)
}

const setCurrentLabel = (id: string) => {
  const item = geometries.value.find((item) => item.id === id)
  const mesh = item?.mesh
  currentTransform.value = {
    x: mesh?.position.x || 0,
    y: mesh?.position.y || 0,
    z: mesh?.position.z || 0,
    scaleX: mesh?.scale.x || 1,
    scaleY: mesh?.scale.y || 1,
    scaleZ: mesh?.scale.z || 1,
  }
}

// const handleCheck = (item: {
//   id: string
//   type: GeometryType
//   mesh: THREE.Mesh
// }) => {
//   checkGeometry(item.id)
//   setTransform(item.mesh)
// }

const handleLoadModel = async () => {
  const res = await downloadModel({
    modelId,
  })
  const url = URL.createObjectURL(res.blob)
  const model = await loadModel(url)

  if (model) {
    addModel(model)
  }
}

const handleSave = async () => {
  try {
    await ElMessageBox.confirm('保存，是否继续？', '提示', {
      type: 'success',
    })
    let json = geometries.value.map((item) => {
      let operation = 'create'
      if (labelData.value.find((e) => e.id === item.id)) {
        operation = 'update'
      }
      return {
        operation: operation as Operation,
        id: operation === 'create' ? '' : item.id,
        labelName: item.name,
        labelType: item.type,
        locationX: item.mesh.position.x,
        locationY: item.mesh.position.y,
        locationZ: item.mesh.position.z,
        scaleX: item.mesh.scale.x,
        scaleY: item.mesh.scale.y,
        scaleZ: item.mesh.scale.z,
      }
    })
    const deleteList = labelData.value.filter(
      (e) => !json.find((j) => e.id === j.id),
    )
    json = json.concat(
      deleteList.map((e) => {
        return {
          ...e,
          operation: 'delete',
        }
      }),
    )
    await addLabelSegment(modelId, json)
    ElMessage.success('保存成功')
  } catch (error) {
    console.log(error)
  }
}

const resetStep = () => {
  const step =
    Math.min(modelSize.value.x, modelSize.value.y, modelSize.value.z) / 50
  positionStep.value = step <= 0.01 ? 0.01 : step
  scaleStep.value = step
}

const labelData = ref<
  {
    operation: Operation
    id: string
    labelName: string
    labelType: GeometryType
    locationX: number
    locationY: number
    locationZ: number
    scaleX: number
    scaleY: number
    scaleZ: number
  }[]
>([])
const initLabels = async () => {
  const res = await getLabels(modelId)
  labelData.value = res

  res.forEach((e) => {
    let geometry: THREE.Mesh | null = null
    if (e.labelType === 'box') {
      const item = addBox(
        e.labelName,
        e.id,
        {
          x: e.locationX,
          y: e.locationY,
          z: e.locationZ,
        },
        e.scaleX,
        e.scaleY,
        e.scaleZ,
      )
      geometry = item.mesh
      // id = item.id
    } else if (e.labelType === 'sphere') {
      const item = addSphere(
        e.labelName,
        e.id,
        {
          x: e.locationX,
          y: e.locationY,
          z: e.locationZ,
        },
        e.scaleX,
        e.scaleY,
        e.scaleZ,
      )
      geometry = item.mesh
      // id = item.id
    } else if (e.labelType === 'cylinder') {
      const item = addCylinder(
        e.labelName,
        e.id,
        {
          x: e.locationX,
          y: e.locationY,
          z: e.locationZ,
        },
        e.scaleX,
        e.scaleY,
        e.scaleZ,
      )
      geometry = item.mesh
      // id = item.id
    }
    if (geometry) {
      addLabel(geometry)
    }
  })
}

onMounted(async () => {
  await handleLoadModel()
  try {
    await initLabels()
  } catch (error) {
    console.log(error)
  }
  resetStep()
})
</script>
<style lang="scss" scoped>
.markPage {
  width: 100vw;
  height: 100vh;
  position: absolute;
  top: 0;
  left: 0;
  overflow: hidden;
  .bottom {
    width: 100vw;
    height: calc(100vh - 40px);
    display: flex;
    .left {
      width: 300px;
      background-color: #2c2c2c;
      .viewpoint {
        width: 280px;
        height: 280px;
        margin: 10px auto;
      }
    }
    .right {
      width: calc(100% - 300px);
    }
  }
}
.container {
  width: 100%;
  height: 100%;
  position: relative;
  background: url('@/assets/backgroud.jpg') no-repeat center center;
  background-size: cover;
  // color: #e6e7ee;
  .mainViewport {
    width: calc(100% * 2 / 3);
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;
    border: 1px solid #000;
  }
  .viewportOne {
    width: calc(100% * 1 / 3);
    height: calc((100vh - 40px) / 3);
    position: absolute;
    top: 0;
    right: 0;
    border: 1px solid #000;
    span {
      user-select: none;
    }
  }
  .viewportTwo {
    width: calc(100% * 1 / 3);
    height: calc((100vh - 40px) / 3);
    position: absolute;
    top: calc(1 / 3 * (100vh - 40px));
    right: 0;
    border: 1px solid #000;
    span {
      user-select: none;
    }
  }
  .viewportThree {
    width: calc(100% * 1 / 3);
    height: calc((100vh - 40px) / 3);
    position: absolute;
    top: calc(2 / 3 * (100vh - 40px));
    right: 0;
    border: 1px solid #000;
    span {
      user-select: none;
    }
  }
}
.labels {
  width: 300px;
  height: calc(100vh - 300px - 40px);
  // background-color: pink;
  position: absolute;
  left: 0;
  top: 340px;
  .title {
    font-size: 18px;
    line-height: 32px;
  }
  .list {
    height: calc(100% - 32px);
  }
}
</style>
